package com.cc.gresturelock;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * Created by CC on 2016/8/27.
 */

public class LockView extends BaseView {

    private final int DEF_COLUMN = 3;
    private final int DEF_LINE_WIDTH = 10;

    private Path path;
    private Paint paint;

    private int minKeyCount = 3;
    private int verifyCounnt = 5;
    private int column = 0;
    float lineWidth = 10;
    private int keyRectWidth;
    private int keyRectHeight;

    private List<Rect> keyRectList;
    private List<Rect> linkedRect;
    private LinkedHashMap<Rect, String> linkedKeyMap;
    private PointF currentTouchPoint;
    private Drawable keyDrawable;
    private int[] STATE_NORMAL = new int[]{};
    private int[] STATE_LINKED = new int[]{android.R.attr.state_selected};
    private int repeatCount;

    public static final int MODE_VERIFY = 0, MODE_SETTING = 1;
    private int mode = MODE_VERIFY;

    public LockView(Context context) {
        super(context);
    }

    public LockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        System.out.println("hehe");
        init(context, attrs);
    }

    public LockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    protected void init(Context context, AttributeSet attrs) {

        int lineColor = context.getResources().getColor(R.color.colorPrimary);
        if (attrs != null) {
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LockView);
            column = ta.getInt(R.styleable.LockView_column, DEF_COLUMN);
            lineColor = ta.getColor(R.styleable.LockView_line_color, lineColor);
            lineWidth = ta.getDimension(R.styleable.LockView_line_width, DEF_LINE_WIDTH);
            ta.recycle();
        }
        System.out.println("init:lineWidth:" + lineWidth + ",init:colunm:" + column);
        keyRectList = new ArrayList<>();
        linkedKeyMap = new LinkedHashMap<>();
        linkedRect = new ArrayList<>();
        currentTouchPoint = new PointF();

        paint = new Paint();
        path = new Path();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(lineWidth);
        paint.setColor(lineColor);
        paint.setStyle(Paint.Style.STROKE);

        //连线的圆角大小
        paint.setPathEffect(new CornerPathEffect(10));

        keyRectWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics());
        keyRectHeight = keyRectWidth;
        keyDrawable = getResources().getDrawable(R.drawable.key);
    }

    Rect firstTouchRect;
    private int curLinkedPosition;
    private String tempKeys;

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Rect rect = getTouchedRect(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                if (rect != null) {
                    linkedRect.add(firstTouchRect = rect);
                    linkedKeyMap.put(rect, String.valueOf(curLinkedPosition));
                    path.reset();
                    path.moveTo(event.getX(), event.getY());
                    currentTouchPoint.set(event.getX(), event.getY());
                    invalidate();
                }
                return true;
            case MotionEvent.ACTION_MOVE:
                if (linkedRect.size() != 0) {
                    currentTouchPoint.set(event.getX(), event.getY());
                }

                if (rect == null || linkedKeyMap.get(rect) != null)
                    break;
                linkedRect.add(rect);
                linkedKeyMap.put(rect, String.valueOf(curLinkedPosition));
                break;
            case MotionEvent.ACTION_UP:
                if (linkedRect.size() == 0) {
                    break;
                }
                //验证模式
                if (mode == MODE_VERIFY) {
                    boolean isEqual = getGestureValue().equals(getLocalKyes());
                    Toast.makeText(getContext(), isEqual ? "解锁成功" : "解锁失败", Toast.LENGTH_SHORT).show();
                    if (onGestureListener != null) {
                        if (isEqual) {
                            onGestureListener.onSuccess();
                        } else {
                            onGestureListener.onFailed();
                        }
                    }
                } else {
                    if (linkedRect.size() < minKeyCount) {
                        Toast.makeText(getContext(), "请至少连接3个点", Toast.LENGTH_SHORT).show();
                    } else {
                        if (repeatCount > 0) {
                            boolean isEqual = getKyes().equals(tempKeys);
                            Toast.makeText(getContext(), isEqual ? "设置成功" : "设置失败，请重新绘制", Toast.LENGTH_SHORT).show();
                            if (isEqual) {
                                saveKey();
                            }
                            reset();
                            if (onGestureListener != null) {
                                if (isEqual) {
                                    //设置成功
                                    onGestureListener.onSuccess();
                                } else {
                                    onGestureListener.onFailed();
                                }
                            }
                        } else {
                            Toast.makeText(getContext(), "请再次绘制", Toast.LENGTH_SHORT).show();
                            tempKeys = getKyes();
                            repeatCount++;
                        }
                    }

                    if (onSetGestureListener != null) {
                        onSetGestureListener.onSetGesture(linkedRect);
                    }
                }

                linkedRect.clear();
                linkedKeyMap.clear();
                break;
        }
        invalidate();
        return super.dispatchTouchEvent(event);
    }

    private void saveKey() {
        try {
            OutputStream os = getContext().openFileOutput("gesture", Context.MODE_PRIVATE);
            String value = getKyes();
            os.write(value.getBytes());
            System.out.println("keys:" + getLocalKyes());
            repeatCount++;
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(getContext(), "设置出错", Toast.LENGTH_SHORT).show();
        }
    }

    private String getKyes() {
        String value = getGestureValue();
        if (gestureValueCover != null) {
            value = gestureValueCover.encodeValue(value);
        }
        return value;
    }

    private String getGestureValue() {
        return linkedKeyMap.values().toString();
    }

    /**
     * 读取文件存储中的手势
     *
     * @return
     */
    private String getLocalKyes() {
        try {
            InputStream inputStream = getContext().openFileInput("gesture");
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String value = reader.readLine();
            if (gestureValueCover != null) {
                return gestureValueCover.decodeValue(value);
            } else {
                return value;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void clearGesture() {
        File file = new File(getContext().getFilesDir() + "/gesture");
        file.delete();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.rewind();
        if (keyDrawable == null) {
            for (Rect rect : keyRectList) {
                canvas.drawCircle(rect.centerX(), rect.centerY(), 10, paint);
            }
        } else {
            for (Rect rect : keyRectList) {
                keyDrawable.setBounds(rect);
                if (linkedKeyMap.get(rect) != null) {
                    keyDrawable.setState(STATE_LINKED);
                } else {
                    keyDrawable.setState(STATE_NORMAL);
                }
                keyDrawable.draw(canvas);
            }
        }
        if (linkedRect.size() > 0) {
            path.moveTo(linkedRect.get(0).centerX(), linkedRect.get(0).centerY());
            for (Rect rect : linkedRect) {
                path.lineTo(rect.centerX(), rect.centerY());
                canvas.drawPath(path, paint);
            }
            path.lineTo(currentTouchPoint.x, currentTouchPoint.y);
            canvas.drawPath(path, paint);
        }
    }

    @Nullable
    private Rect getTouchedRect(MotionEvent event) {
        //预判断位置,减少循环提升效率
        int start = 0;
        for (int j = 0; j < column; j++) {
            if (event.getY() < keyRectList.get(column * j).bottom) {
                start = column * j;
                break;
            }
        }
        for (int i = start; i < start + column; i++) {
            Rect rect = keyRectList.get(i);
            if (rect.contains((int) event.getX(), (int) event.getY())) {
                curLinkedPosition = i;
                return rect;
            }
        }
        return null;
    }

    public void reset() {
        linkedKeyMap.clear();
        linkedRect.clear();
        repeatCount = 0;
//        clearGesture();
    }

    /**
     * 验证手势密码
     */
    public void setMode(int mode) {
        this.mode = mode;
    }

    /**
     * 是否已经设置过手势密码
     *
     * @return
     */
    public boolean hasKey() {
        return getLocalKyes() != null;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        calWidthRect();
    }

    /**
     * 将宽高平分为@colunm份
     */
    private void calWidthRect() {
        System.out.println("hehe:" + lineWidth + ",hehecolunm::" + column);
        keyRectList.clear();
        ArrayList<Rect> tempKeyParent = new ArrayList<>();
        int rectWidth = (int) (mainRect.width() / column);
        int rectHeight = (int) (mainRect.height() / column);
        for (int k = 0; k < column; k++) {
            for (int i = 0; i < column; i++) {
                Rect rect = new Rect(rectWidth * i, rectHeight * k, rectWidth * (i + 1), rectHeight * (k + 1));
                tempKeyParent.add(rect);
            }
        }
        for (Rect rect : tempKeyParent) {
            int left = (rect.left + (rect.width() - keyRectWidth) / 2);
            int top = (rect.top + (rect.height() - keyRectHeight) / 2);
            Rect keyRect = new Rect(left, top, left + keyRectWidth, top + keyRectHeight);
            keyRectList.add(keyRect);
        }
    }

    private OnSetGestureListener onSetGestureListener;

    public void setOnSetGestureListener(OnSetGestureListener onSetGestureListener) {
        this.onSetGestureListener = onSetGestureListener;
    }

    public interface OnSetGestureListener {
        void onSetGesture(List<Rect> Rects);
    }

    private IGestureValueCover gestureValueCover;

    public void setGestureValueCover(IGestureValueCover gestureValueCover) {
        this.gestureValueCover = gestureValueCover;
    }

    public interface IGestureValueCover {

        /**
         * 对手势数据加密
         *
         * @param value
         * @return
         */
        String encodeValue(String value);

        /**
         * 对手机数据解密
         *
         * @param value
         * @return
         */
        String decodeValue(String value);
    }

    private OnGestureListener onGestureListener;

    public void setOnGestureListener(OnGestureListener onGestureListener) {
        this.onGestureListener = onGestureListener;
    }

    public interface OnGestureListener {
        void onSuccess();

        void onFailed();
    }

}
